iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
SideProject30

打造紐時風格的時間線小遊戲系列 第 16

功能製作 卡片提示與卡片區塊移動處理

  • 分享至 

  • xImage
  •  

卡片提示與卡片區塊移動處理代碼解析

我使用一個叫 handleAnswerProcess 的函示,去包含會在作答的時候去處理作答相關的工作,但主要三大功能為

  1. 提示區塊的顯示
  2. 提示區塊的移動,連動其他已作答卡片的位置調整
  3. 作答,放置提示卡片後,整體卡片排版(後續動畫部分)

提示區塊的顯示

if (currentCardState.bottom > timelineElState.top) {
    isShowHint.value = true;
    handleTimelineContainerExtend(true);
} else {
    isShowHint.value = false;
    handleTimelineContainerExtend(false);
}

提示區塊的移動,連動其他已作答卡片的位置調整

calcTimelineEventCenterLines 函數主要是用於計算時間軸上每個事件(timeline event)的中心線位置,並根據當前拖曳的卡片(clue card)的位置來更新 overOutlineCount 的值。overOutlineCount 邏輯處理流程

  1. 首先,計算每個時間軸事件的中心線位置。
  2. 然後,使用 reduce 函數來計算當前拖曳的卡片底部超過了多少個中心線。

示意圖
https://ithelp.ithome.com.tw/upload/images/20231001/20107703QORsooWrsQ.jpg

const calcTimelineEventCenterLines = (timelineEventsElState, currentCardState) => {
    let timelineEventCenterLines = timelineEventsElState.map((el) => window.scrollY + el.top + el.height / 2);
    overOutlineCount.value = timelineEventCenterLines.reduce((acc, cur) => {
        if (currentCardState.bottom > cur) {
            return acc + 1;
        }
        return acc;
    }, 0);
};
  1. 提示框出現的位置 hintPostionTop 來記錄
  2. 每一張作答已作答卡片,要因問題卡片移動的位置調動 hintPostionTop 提示框 和 timelineEventsStyleRaw[ Array ] 來記錄每一張卡片的位置。
if (gameStatus.currentStep === 1) {
        if (currentCardState.bottom > timelineEventsElState[0].bottom) {
            hintPostionTop.value = `${hintDefaultTop + overOutlineCount.value * (timelineEventHeight + timelineEventMarginTop) + hintHeight + timelineEventMarginTop}px`;
        } else {
            hintPostionTop.value = `${hintDefaultTop}px`;
        }
    }

    if (gameStatus.currentStep === 2) {
        //計算每張 timelineEvent 的中心線,如果超過就移動 hint

        calcTimelineEventsCurrentStyles(hintHeight, timelineEventMarginTop);
        hintPostionTop.value = `${hintDefaultTop + overOutlineCount.value * (timelineEventHeight + timelineEventMarginTop)}px`;
    }

    if (gameStatus.currentStep === 3) {
        //計算每張 timelineEvent 的中心線,如果超過就移動 hint
        hintDefaultTop = -40;

        calcTimelineEventsCurrentStyles(hintHeight, timelineEventMarginTop);
        if (overOutlineCount.value === 0) {
            hintPostionTop.value = `${hintDefaultTop}px`;
        } else {
            hintPostionTop.value = `${hintDefaultTop + overOutlineCount.value * (timelineEventHeight + timelineEventMarginTop)}px`;
        }
    }

    if (gameStatus.currentStep > 3) {
        hintDefaultTop = -40;

        calcTimelineEventsCurrentStyles(hintHeight, timelineEventMarginTop);
        if (overOutlineCount.value === 0) {
            hintPostionTop.value = `${hintDefaultTop}px`;
        } else {
            hintPostionTop.value = `${
                hintDefaultTop + (1 * (hintHeight + timelineEventHeight + timelineEventMarginTop)) / 2 + (overOutlineCount.value - 1) * (timelineEventHeight / 2 + timelineEventMarginTop)
            }px`;
        }
    }
const calcTimelineEventsCurrentStyles = (hintHeight, timelineEventMarginTop) => {
    timelineEventsStyleRaw.value.forEach((element, index) => {
        if (index < overOutlineCount.value) {
            timelineEventsStyleRaw.value[index].transform = `translate(-50%, ${currentTimelinePosition.value[index]?.y - (hintHeight + timelineEventMarginTop)}px)`;
        } else {
            timelineEventsStyleRaw.value[index].transform = `translate(-50%, ${currentTimelinePosition.value[index]?.y}px)`;
        }
    });
};

//因為手機長度的限制,當每一個卡片放進作答位置的時候,會更新卡片的排版

currentTimelinePosition

const handleClueCardTouchOff = async (cardIndex, ev) => {
    // 略 
    if (isShowHint.value) {


        handleUpdateTimelinePosition(gameStatus.currentStep);


    } else {
        //略 
    }
    handleTimelineContainerExtend(false);
    document.removeEventListener('touchmove', handleClueCardMove);
};

抓取每一個步驟預設的排版

const handleUpdateTimelinePosition = (currentStep) => {
    // 更新 timelineEvents 的位置
    const updateTimelineEventsPosition = (currentStep, timelineEvents, timelinePositions) => {
        for (let i = 0; i < currentStep; i++) {
            timelineEvents[i].transform = `translate(-50%, ${timelinePositions[i]?.y}px)`;
        }
    };

    currentTimelinePosition.value = clueDefaultPosition[gameStatus.currentStep - 1];
    //略


};

預設遊戲每一個步驟的排版資料,基本上 x 軸都是在中間位置,只有 y 軸有不同高度

[
    [
        {
            "x": "-50%",
            "y": 150
        }
    ],
    [
        {
            "x": "50%",
            "y": 150
        },
        {
            "x": "50%",
            "y": 260
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 150
        },
        {
            "x": "50%",
            "y": 260
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 100
        },
        {
            "x": "50%",
            "y": 160
        },
        {
            "x": "50%",
            "y": 220
        },
        {
            "x": "50%",
            "y": 280
        },
        {
            "x": "50%",
            "y": 340
        },
        {
            "x": "50%",
            "y": 400
        },
        {
            "x": "50%",
            "y": 460
        },
        {
            "x": "50%",
            "y": 520
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 100
        },
        {
            "x": "50%",
            "y": 160
        },
        {
            "x": "50%",
            "y": 220
        },
        {
            "x": "50%",
            "y": 280
        },
        {
            "x": "50%",
            "y": 340
        },
        {
            "x": "50%",
            "y": 400
        },
        {
            "x": "50%",
            "y": 460
        },
        {
            "x": "50%",
            "y": 520
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 100
        },
        {
            "x": "50%",
            "y": 160
        },
        {
            "x": "50%",
            "y": 220
        },
        {
            "x": "50%",
            "y": 280
        },
        {
            "x": "50%",
            "y": 340
        },
        {
            "x": "50%",
            "y": 400
        },
        {
            "x": "50%",
            "y": 460
        },
        {
            "x": "50%",
            "y": 520
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 100
        },
        {
            "x": "50%",
            "y": 160
        },
        {
            "x": "50%",
            "y": 220
        },
        {
            "x": "50%",
            "y": 280
        },
        {
            "x": "50%",
            "y": 340
        },
        {
            "x": "50%",
            "y": 400
        },
        {
            "x": "50%",
            "y": 460
        },
        {
            "x": "50%",
            "y": 520
        }
    ],
    [
        {
            "x": "50%",
            "y": 40
        },
        {
            "x": "50%",
            "y": 100
        },
        {
            "x": "50%",
            "y": 160
        },
        {
            "x": "50%",
            "y": 220
        },
        {
            "x": "50%",
            "y": 280
        },
        {
            "x": "50%",
            "y": 340
        },
        {
            "x": "50%",
            "y": 400
        },
        {
            "x": "50%",
            "y": 460
        },
        {
            "x": "50%",
            "y": 520
        }
    ]
]

ChatGPT 優化建議與自我優化思考

GPT

  1. 代碼重構: handleAnswerProcess 函數過於冗長和複雜。考慮將其拆分成多個小函數。
  2. DRY 原則: 避免重複的代碼。例如,hintPostionTop.value 的設置在多個地方重複。
  3. 命名規範: 使用更具描述性的變數和函數名稱。
  4. 使用常數: 對於像 75、17 這樣的魔術數字,最好使用有意義的常數名稱來替代。

自我

  1. 是否可將卡片預設位置公式化,而不是用 JSON 檔寫死處理

上一篇
功能製作 複雜的時間軸拉伸處理
下一篇
功能製作 計分 分數回饋
系列文
打造紐時風格的時間線小遊戲30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言